//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
namespace LargoCommon.Music
{
using LargoCommon.Interfaces;
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
///
/// Figural Structure.
///
public class FiguralStructure : GeneralOwner, IComparable, IGeneralStruct
{
#region Fields
///
/// Structural Code.
///
private string structuralCode;
///
/// General system.
///
private GeneralSystem gsystem; //// readonly
/// Number of nonzero bits, number of rotations.
private byte level;
/// List of elements.
private Collection elemList;
/// List of differences.
private Collection diffList;
#endregion
#region Constructors
/// Initializes a new instance of the FiguralStructure class. Serializable.
public FiguralStructure() {
this.elemList = new Collection();
}
///
/// Initializes a new instance of the FiguralStructure class.
///
/// The given system.
/// Number of instance.
public FiguralStructure(GeneralSystem givenSystem, decimal number)
: this() {
Contract.Requires(givenSystem != null);
this.gsystem = givenSystem;
this.DecimalNumber = number;
this.SetElements(); // 20081224
//// this.CheckInstance();
}
///
/// Initializes a new instance of the FiguralStructure class.
///
/// The given system.
/// Structural code.
public FiguralStructure(GeneralSystem givenSystem, string givenStructuralCode)
: this() {
Contract.Requires(givenSystem != null);
this.gsystem = givenSystem;
for (byte e = 0; e < this.gsystem.Order; e++) {
this.ElementList.Add(0);
}
this.SetStructuralCode(givenStructuralCode);
//// this.SetElements(); // 20081224
//// 2014/12 Time optimization
//// this.CheckInstance();
}
/// Initializes a new instance of the FiguralStructure class.
/// Figural structure.
public FiguralStructure(FiguralStructure structure)
: this() {
Contract.Requires(structure != null);
this.gsystem = structure.GSystem;
this.level = structure.Level;
this.GLevel = structure.GLevel;
this.SetElements();
//// 2014/12 Time optimization
//// this.CheckInstance();
}
#endregion
#region Properties
/// Gets or sets DecimalNumber.
/// Property description.
public decimal DecimalNumber { get; set; }
/// Gets or sets abstract G-System.
/// Property description.
[XmlIgnore]
public GeneralSystem GSystem {
get {
Contract.Ensures(Contract.Result() != null);
Contract.Ensures(Contract.Result().Order > 0);
if (this.gsystem == null) {
throw new InvalidOperationException("G-system is null.");
}
return this.gsystem;
}
set => this.gsystem = value;
}
/// Gets or sets modality.
/// Property description.
[XmlIgnore]
public IGeneralStruct Modality { get; set; }
/// Gets or sets level of the structure. Sum of digits.
/// Property description.
public int GLevel { get; set; }
/// Gets or sets level of the structure.
/// Property description.
[XmlAttribute]
public byte Level {
get => this.level;
set {
this.level = value;
this.SetProperty(GenProperty.Level, this.level);
}
}
///
/// Gets or sets the occurrence - for statistical reasons (material,...). May be moved to properties...
///
///
/// The occurrence.
///
public int Occurrence { get; set; }
/// Gets variability.
/// Property description.
public float Variability { get; private set; }
/// Gets elements of the figure.
/// Property description.
[XmlIgnore]
public Collection ElementList {
get {
Contract.Ensures(Contract.Result>() != null);
if (this.elemList == null) {
throw new InvalidOperationException("List of elements is null.");
}
return this.elemList;
}
}
/// Gets list of differences.
/// Property description.
[XmlIgnore]
public Collection DiffList {
get {
Contract.Ensures(Contract.Result>() != null);
if (this.diffList == null) {
this.MakeDifferences();
}
if (this.diffList == null) {
throw new InvalidOperationException("List of differences is null.");
}
return this.diffList;
}
}
/// Gets differences.
/// Property description.
public string DiffSchema => this.DifferenceString();
/// Gets differences.
/// Property description.
public string PositiveElementSchema => this.PositiveElementString();
#endregion
///
/// Gets the Structural Code.
///
/// Returns value.
/// Property description.
public string GetStructuralCode {
get {
//// 2014/12 Time optimization - was prepared here (why not used?)
if (string.IsNullOrEmpty(this.structuralCode)) {
this.structuralCode = this.DetermineStructuralCode();
}
return this.structuralCode;
}
}
#region Static Functions
/// Rotate the number, moving first bit to the end.
/// Degree of system.
/// Order of system.
/// Number of instance.
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public static decimal TransposeLeft(byte givenDegree, byte order, decimal number) {
var size = (decimal)Math.Pow(givenDegree, order);
return (number * givenDegree) % size;
}
/// Returns empty number with only bit at position 'i' set.
/// Element of system.
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public static long BitAt(byte element) {
return (long)1 << element;
}
/// Returns if selected bit is ON.
/// Number of instance.
/// Element of system.
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public static bool ElementIsOn(decimal number, byte element) {
return ((long)number & BitAt(element)) != 0;
}
///
/// Determine Level.
///
/// System order.
/// Structural Number.
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public static byte DetermineLevel(byte sysOrder, decimal number) {
byte level = 0;
for (byte e = 0; e < sysOrder; e++) {
if (ElementIsOn(number, e)) {
level++;
}
}
return level;
}
#endregion
#region Static operators
//// TICS rule 7@526: Reference types should not override the equality operator (==)
//// public static bool operator ==(FiguralStructure structure1, FiguralStructure structure2) { return object.Equals(structure1, structure2); }
//// public static bool operator !=(FiguralStructure structure1, FiguralStructure structure2) { return !object.Equals(structure1, structure2); }
//// but TICS rule 7@530: Class implements interface 'IComparable' but does not implement '==' and '!='.
///
/// Implements the operator <.
///
/// The object1.
/// The object2.
///
/// Returns value.
///
public static bool operator <(FiguralStructure object1, FiguralStructure object2) {
if (object1 != null && object2 != null && (object1.DecimalNumber > 0 || object2.DecimalNumber > 0)) {
return object1.DecimalNumber < object2.DecimalNumber;
}
return false;
}
///
/// Implements the operator >=.
///
/// The object1.
/// The object2.
///
/// Returns value.
///
public static bool operator >=(FiguralStructure object1, FiguralStructure object2) {
if (object1 != null && object2 != null && (object1.DecimalNumber > 0 || object2.DecimalNumber > 0)) {
return object1.DecimalNumber >= object2.DecimalNumber;
}
return false;
}
///
/// Implements the operator <=.
///
/// The object1.
/// The object2.
///
/// Returns value.
///
public static bool operator <=(FiguralStructure object1, FiguralStructure object2) {
if (object1 != null && object2 != null && (object1.DecimalNumber > 0 || object2.DecimalNumber > 0)) {
return object1.DecimalNumber <= object2.DecimalNumber;
}
return false;
}
///
/// Implements the operator >.
///
/// The object1.
/// The object2.
///
/// Returns value.
///
public static bool operator >(FiguralStructure object1, FiguralStructure object2) {
if (object1 != null && object2 != null && (object1.DecimalNumber > 0 || object2.DecimalNumber > 0)) {
return object1.DecimalNumber > object2.DecimalNumber;
}
return false;
}
#endregion
/// Makes a deep copy of the FiguralStructure object.
/// Returns object.
public override object Clone() {
return new FiguralStructure(this.GSystem, this.GetStructuralCode);
}
#region Comparison
/// Support sorting according to level and number.
/// Object to be compared.
/// Returns value.
public override int CompareTo(object value) {
if (!(value is FiguralStructure structure)) {
return 0;
}
if (this.Level < structure.Level) {
return -1;
}
return this.Level > structure.Level ?
1 :
string.CompareOrdinal(this.ElementString(), structure.ElementString());
//// This kills the DataGrid
//// throw new ArgumentException("Object is not a FiguralStructure");
}
/// Test of equality.
/// Object to be compared.
/// Returns value.
public override bool Equals(object obj) {
//// check null (this pointer is never null in C# methods)
if (object.ReferenceEquals(obj, null)) {
return false;
}
if (object.ReferenceEquals(this, obj)) {
return true;
}
if (this.GetType() != obj.GetType()) {
return false;
}
return this.CompareTo(obj) == 0;
}
/// Support of comparison.
/// Returns value.
public override int GetHashCode() {
return this.ElementString().GetHashCode();
}
#endregion
/// Test of emptiness.
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public virtual bool IsEmptyStruct() {
return this.level == 0;
}
/// Validity test.
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public virtual bool IsValidStruct() {
return true;
}
#region Element gettings
/// Returns if selected bit is ON.
/// Requested element.
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public bool IsOn(byte element) {
return element < this.ElementList.Count && this.ElementList[element] != 0;
}
///
/// Determines whether the specified element is one.
///
/// The element.
///
/// true if the specified element is one; otherwise, false.
///
[JetBrains.Annotations.PureAttribute]
public bool IsOne(byte element) {
return element < this.ElementList.Count && this.ElementList[element] == 1;
}
/// Returns if selected bit is OFF.
/// Requested element.
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public bool IsOff(byte element) {
if (element < this.ElementList.Count) {
return this.ElementList[element] == 0;
}
return false;
}
/// Returns if selected bit is a start of tone.
/// Requested element.
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public bool IsToneStart(byte element) {
if (element < this.ElementList.Count) {
return this.ElementList[element] == 1;
}
return false;
}
/// Returns if selected bit is a start of pause.
/// Requested element.
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public bool IsPauseStart(byte element) {
if (element < this.ElementList.Count) {
return this.ElementList[element] == 2;
}
return false;
}
///
/// Returns corresponding binary structure.
///
/// If set to true [break for rests].
///
/// Returns value.
///
[JetBrains.Annotations.PureAttribute]
public BinaryStructure BinaryStructure(bool breakForRests) {
var sys = new GeneralSystem(2, this.GSystem.Order);
var binStr = new BinaryStructure(sys, (string)null);
for (byte e = 0; e < this.GSystem.Order; e++) {
//// 2015/01
if (breakForRests) {
if (this.IsOn(e)) {
binStr.On(e);
}
}
else {
if (this.IsOne(e)) {
binStr.On(e);
}
}
}
binStr.DetermineLevel();
return binStr;
}
///
/// Bits the array.
///
/// if set to true [break for rests].
///
/// Returns value.
///
public BitArray BitArray(bool breakForRests) {
var bitArray = new BitArray(this.GSystem.Order);
for (byte e = 0; e < this.GSystem.Order; e++) {
if (breakForRests) {
if (this.IsOn(e)) {
bitArray[e] = true;
}
}
else {
if (this.IsOne(e)) {
bitArray[e] = true;
}
}
}
return bitArray;
}
/// Returns number of bits in selected Range, that are ON.
/// First element.
/// Last element.
/// Number of bits.
[JetBrains.Annotations.PureAttribute]
public byte IsOnInRange(byte elementFrom, byte elementTo) {
byte s = 0;
for (var e = elementFrom; e <= elementTo; e++) {
if (this.IsOn(e)) {
s++;
}
}
return s;
}
#endregion
#region Element settings
///
/// Sets selected bit to value.
///
/// Number of element.
/// Value of element.
public void SetElement(byte element, short value) {
if (element < this.ElementList.Count) {
this.ElementList[element] = value;
}
}
///
/// Set Element List.
///
/// The given list.
public void SetElementList(Collection givenList) {
this.elemList = givenList;
this.CompleteFromElements();
this.DetermineBehavior();
}
#endregion
/// Determine and sets the level property.
public void DetermineLevel() {
this.Level = 0;
this.GLevel = 0;
var order = this.GSystem.Order;
for (byte e = 0; e < order; e++) {
if (e >= this.ElementList.Count || this.ElementList[e] <= 0) {
continue;
}
this.GLevel += this.ElementList[e];
this.Level++;
}
this.Properties[GenProperty.Level] = this.Level; // used with Qualifiers
}
///
/// Determine and sets the level property.
///
/// Given bit of the structure.
/// Returns value.
public byte LevelOfBit(byte givenBit) {
if (givenBit == 0) {
return 0;
}
var bitLevel = -1;
//// byte order = this.GSystem.Order;
for (byte e = 0; e <= givenBit; e++) {
if (e < this.ElementList.Count && this.ElementList[e] > 0) {
bitLevel++;
}
}
var lev = (byte)((bitLevel >= 0) ? bitLevel : 0);
return lev;
}
///
/// Determine and sets the level property.
///
public void DetermineINumber() {
decimal num = 0;
if (this.ElementList.Count > 0) {
var order = this.GSystem.Order;
var degree = this.GSystem.Degree;
for (var e = (short)(order - 1); e >= 0; e--) {
if (e < this.ElementList.Count && num < decimal.MaxValue / degree) { //// Uff
num = (num * this.GSystem.Degree) + (byte)this.ElementList[e];
}
}
}
this.DecimalNumber = num;
}
/// Evaluate properties of the structure. Used in descendant objects.
/// Must be virtual, because of call from StructuralVariety.
public virtual void DetermineBehavior() {
}
/// Make list of differences.
public void MakeDifferences() {
this.diffList = new Collection();
if (this.ElementList.Count == 0) {
return;
}
byte p = 0, r = 0;
var order = this.GSystem.Order;
for (byte e = 0; e < order - 1; e++) {
if (e + 1 >= this.ElementList.Count) {
continue;
}
p = (byte)this.ElementList[e];
r = (byte)this.ElementList[(byte)(e + 1)];
this.diffList.Add((short)(r - p));
}
if (order - 1 < this.ElementList.Count) {
p = (byte)this.ElementList[(byte)(order - 1)];
}
if (this.ElementList.Count > 0) {
r = (byte)this.ElementList[0];
}
this.diffList.Add((short)(r - p));
}
///
/// Complete From Elements.
///
public virtual void CompleteFromElements() {
this.DetermineLevel();
////this.DetermineINumber();
this.ComputeVariability();
this.MakeDifferences();
}
#region String representation
/// List of figure elements.
/// Returns value.
public virtual string ElementString() {
var s = new StringBuilder();
var cnt = this.ElementList.Count;
var order = this.GSystem.Order;
for (byte e = 0; e < cnt; e++) {
s.Append(this.ElementList[e]);
if (e < order - 1) {
s.Append(",");
}
}
return s.ToString();
}
/// List of figure elements.
/// Returns value.
public string PositiveElementString() {
const byte shift = 50;
var s = new StringBuilder();
var cnt = this.ElementList.Count;
for (byte e = 0; e < cnt; e++) {
s.AppendFormat(CultureInfo.CurrentCulture, "{0,2}", (this.ElementList[e] + shift).ToString("D", CultureInfo.CurrentCulture.NumberFormat));
}
return s.ToString();
}
/// List of figure elements.
/// Returns value.
public string InverseElementString() {
var s = new StringBuilder();
for (var e = (short)(this.GSystem.Order - 1); e >= 0; e--) {
if (e >= this.ElementList.Count) {
continue;
}
s.Append(this.ElementList[e]);
if (e < this.GSystem.Order - 1) {
s.Append(",");
}
}
return s.ToString();
}
/// List of figure Differences.
/// Returns value.
public string DifferenceString() {
if (this.DiffList == null || this.DiffList.Count == 0) {
return string.Empty;
}
var s = new StringBuilder();
s.Append("(");
for (byte e = 0; e < this.GSystem.Order; e++) {
if (e >= this.diffList.Count) {
continue;
}
s.Append(this.diffList[e]);
if (e < this.GSystem.Order - 1) {
s.Append(",");
}
}
s.Append(")");
return s.ToString();
}
/// String representation of the object.
/// Returns value.
public override string ToString() {
var s = new StringBuilder();
//// s.Append("<" + this.Data.ToString(System.Globalization.CultureInfo.CurrentCulture.NumberFormat) + ">\t"); //// "D", System.Globalization.CultureInfo.CurrentCulture.NumberFormat
s.Append("L" + this.Level.ToString("D", CultureInfo.CurrentCulture.NumberFormat));
s.Append(" ");
s.Append(this.ElementString());
s.Append(" ");
return s.ToString();
}
#endregion
#region Structural code
/// Determine and sets the level property.
/// Structural code.
public void SetStructuralCode(string givenStructuralCode) {
this.structuralCode = givenStructuralCode;
if (string.IsNullOrWhiteSpace(this.structuralCode)) {
return;
}
//// Temporary if (this.structuralCode.Length == 1) { return; }
var structCode = this.structuralCode.Contains('*') ? UnpackCode(this.structuralCode) : this.structuralCode;
this.Level = 0;
this.GLevel = 0;
this.elemList = new Collection();
//// byte order = this.GSystem.Order; //// givenStructuralCode.Count;
var codes = structCode.Split(',');
// ReSharper disable once LoopCanBePartlyConvertedToQuery
foreach (var code in codes) {
if (string.IsNullOrEmpty(code)) {
continue;
}
var element = short.Parse(code, CultureInfo.CurrentCulture);
if (element < 0) {
continue;
}
if (this.ElementList.Count >= this.GSystem.Order) {
continue; //// 2013/01
}
this.ElementList.Add(element);
if (element <= 0) {
continue;
}
this.Level += 1;
this.GLevel += element;
}
//// Used by Qualifiers
this.Properties[GenProperty.Level] = this.Level;
}
/// Determine and sets the level property.
/// Returns value.
public string DetermineStructuralCode() {
var order = this.GSystem.Order;
if (order <= 1) {
return null;
}
//// 2014/12 Time optimization
var sb = new StringBuilder();
foreach (var c in this.ElementList) {
sb.Append(c.ToString(CultureInfo.CurrentCulture));
sb.Append(",");
}
sb.Remove(sb.Length - 1, 1);
var structCode = sb.ToString();
/* Time optimization
string structCode = this.ElementList[0].ToString(CultureInfo.CurrentCulture);
for (byte e = 1; e < order; e++) {
if (e >= this.ElementList.Count) {
break;
}
structCode += "," + this.ElementList[e].ToString(CultureInfo.CurrentCulture);
}
*/
//// bool packedCodes = true;
return structCode.Length > 0 ? PackCode(structCode) : structCode;
}
#endregion
#region Computation of properties
/// Determine and sets the variability property.
public void ComputeVariability() {
var me = this.MeanElement();
var value = (float)0.0;
var order = this.GSystem.Order;
for (byte e = 0; e < order; e++) {
if (e < this.ElementList.Count) {
value = value + (float)Math.Pow(Math.Abs(this.ElementList[e] - me), 2.0);
}
}
this.Variability = 100 * (float)Math.Sqrt(value) / order; // *100/(double)order;
}
#endregion
/// Determine and sets the elements and level property.
public void SetElements() { //// protected
var num = this.DecimalNumber;
this.Level = 0;
this.GLevel = 0;
this.elemList = new Collection();
var order = this.GSystem.Order;
var degree = this.GSystem.Degree;
for (byte e = 0; e < order; e++) {
var rest = num % degree;
if (rest >= 0) {
this.ElementList.Add((byte)rest);
if (e < this.ElementList.Count && this.ElementList[e] > 0) {
this.GLevel += this.ElementList[e];
this.Level += 1;
}
}
num = (num - rest) / this.GSystem.Degree;
}
//// Used by Qualifiers
this.Properties[GenProperty.Level] = this.Level;
}
///
/// Similarities the value.
///
/// The given structure.
/// Returns value.
public int SimilarityValue(FiguralStructure givenStructure) {
var order = Math.Min(this.GSystem.Order, givenStructure.GSystem.Order);
var value = 0;
for (byte e = 0; e < order; e++) {
if (e >= this.ElementList.Count || this.ElementList[e] <= 0) {
continue;
}
if (e >= givenStructure.ElementList.Count || givenStructure.ElementList[e] <= 0) {
continue;
}
if (this.ElementList[e] == givenStructure.ElementList[e]) {
value++;
}
}
return (100 * value) / order;
}
/// Mean distance of nonzero bits.
/// Returns value.
protected float MeanElement() {
var order = this.GSystem.Order;
var value = (float)this.GLevel / order;
return value < 1 ? 1 : value;
}
/// Determine and sets the elements and level property.
protected void SetElementsFromDifferences() {
decimal num = 0; //// this.Data;
this.Level = 0;
this.GLevel = 0;
this.elemList = new Collection();
this.diffList = new Collection();
short sum = 0;
var order = this.GSystem.Order;
var degree = this.GSystem.Degree;
var halfDegree = (short)(degree / 2);
for (byte e = 0; e < order; e++) {
var eval = (byte)(num % degree);
var diff = (short)(eval - halfDegree);
this.diffList.Add(diff);
sum += diff;
this.ElementList.Add(sum);
if (e >= this.ElementList.Count) {
continue;
}
if (this.ElementList[e] != 0) {
this.GLevel += this.ElementList[e];
this.Level += 1;
}
num = num / degree;
}
this.Properties[GenProperty.Level] = this.Level; // used with Qualifiers
}
#region Code packing
///
/// Unpacks the code.
///
/// The packed code.
/// Returns value.
private static string UnpackCode(string packedCode) {
Contract.Requires(packedCode != null);
//// 2014/12 Time optimization
var sb = new StringBuilder();
//// string structCode = string.Empty;
var packedCodes = packedCode.Split(',');
Array.ForEach(
packedCodes,
pc => {
if (pc.Contains('*')) {
var position = pc.IndexOf('*');
var cnt = int.Parse(pc.Substring(0, position), CultureInfo.CurrentCulture);
var code = pc.Substring(position + 1);
for (var i = 1; i <= cnt; i++) {
sb.Append(code);
sb.Append(",");
}
}
else {
sb.Append(pc);
sb.Append(",");
}
});
return sb.ToString();
}
///
/// Packs the code.
///
/// The code.
/// Returns value.
private static string PackCode(string structCode) {
Contract.Requires(structCode != null);
//// 2014/12 Time optimization
var sb = new StringBuilder();
var codes = structCode.Split(',');
var lastCode = codes[0];
var cnt = 0;
Array.ForEach(
codes,
code => {
if (string.CompareOrdinal(code, lastCode) != 0) {
if (cnt > 1) {
sb.Append(cnt.ToString(CultureInfo.CurrentCulture));
sb.Append("*");
}
sb.Append(lastCode);
sb.Append(",");
lastCode = code;
cnt = 0;
}
cnt++;
});
if (cnt > 1) {
sb.Append(cnt.ToString(CultureInfo.CurrentCulture));
sb.Append("*");
}
sb.Append(lastCode);
return sb.ToString();
}
#endregion
/* Time optimization - without the check
///
/// Check Instance.
///
private void CheckInstance() {
if (this.gsystem == null) {
throw new ArgumentException("Empty gsystem!");
}
} */
}
}